// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using Antlr4.Runtime;
using static Common.Grammar.Antlr.CommonParser;

namespace SnapObjects.Expressions
{
    internal class LiteralExpressionVisitor<T, TContext> :
        NodeExpressionVisitor<T, TContext> where TContext : ParserRuleContext
    {
        public LiteralExpressionVisitor(IServiceProvider services) : base(services)
        {
        }

        public override T Visit(
            IExpressionVisitor<T> treeVisitor,
            TContext context,
            ParserOptions<T> options)
        {
            var text = context.GetText();

            switch (context.Start.Type)
            {
                case var t when t == this.TypeInteger:
                {
                    if (Int32.TryParse(text, out var value))
                    {
                        return _provider.Constant(value, typeof(int));
                    }
                }
                break;
                case var t when t == this.TypeFloat:
                {
                    var temp = text.TrimEnd('f');

                    if (Single.TryParse(temp, out var value))
                    {
                        return _provider.Constant(value, typeof(float));
                    }
                }
                break;
                case var t when t == this.TypeDouble:
                {
                    var temp = text.TrimEnd('d');

                    if (Double.TryParse(temp, out var value))
                    {
                        return _provider.Constant(value, typeof(double));
                    }
                }
                break;
                case var t when t == this.TypeDecimal:
                {
                    var temp = text.TrimEnd('m');

                    if (Decimal.TryParse(temp, out var value))
                    {
                        return _provider.Constant(value, typeof(decimal));
                    }
                }
                break;
                case var t when t == this.TypeString:
                {
                    var result = String.Empty;

                    if ((text.StartsWith("'") && text.EndsWith("'")) ||
                        (text.StartsWith("\"") && text.EndsWith("\"")))
                    {
                        result = text.Substring(1, text.Length - 2);
                    }
                    else if (text.StartsWith("\\\'") && text.EndsWith("\\\'"))
                    {
                        result = text.Substring(2, text.Length - 4);
                    }
                    else if (text.StartsWith("\\\"") && text.EndsWith("\\\""))
                    {
                        result = text.Substring(2, text.Length - 4);
                    }

                    if (text.StartsWith("'") && text.EndsWith("'") && text.Length == 1)
                    {
                        if (Char.TryParse(result, out var value))
                        {
                            return _provider.Constant(value, typeof(char));
                        }
                    }
                    else
                    {
                        return _provider.Constant(result, typeof(string));
                    }
                }
                break;
                case var t when t == this.TypeDate:
                {
                    if (DateTime.TryParse(text, out var value))
                    {
                        return _provider.Constant(value, typeof(DateTime));
                    }
                }
                break;
                case var t when t == this.TypeTime:
                {
                    if (TimeSpan.TryParse(text, out var value))
                    {
                        return _provider.Constant(value, typeof(TimeSpan));
                    }
                }
                break;

                case var t1 when t1 == this.TypeTrue:
                case var t2 when t2 == this.TypeFalse:
                {
                    if (Boolean.TryParse(text, out var value))
                    {
                        return _provider.Constant(value, typeof(bool));
                    }
                }
                break;

                default: throw new InternalParseException($"literal type: {context.Start.Type}");
            }

            throw new InvalidCastException($"literal: {text}");
        }

        protected virtual int TypeInteger => TOKEN_CONST_INTEGER;
        protected virtual int TypeFloat => TOKEN_CONST_FLOAT;
        protected virtual int TypeDouble => TOKEN_CONST_DOUBLE;
        protected virtual int TypeDecimal => TOKEN_CONST_DECIMAL;
        protected virtual int TypeString => TOKEN_CONST_STRING;
        protected virtual int TypeDate => TOKEN_CONST_DATE;
        protected virtual int TypeTime => TOKEN_CONST_TIME;
        protected virtual int TypeTrue => TOKEN_KEY_TRUE;
        protected virtual int TypeFalse => TOKEN_KEY_FALSE;
    }
}
